/*
* Copyright © Shanghai Awinic Technology Co., Ltd. 2020-2020. All rights reserved.
* Description: UART driver related library function files.
* Date： 20201126
* Awinic_Version： aw_soc_driver_v1.0.0
*/
#include "compile_option.h"
#ifdef AW_86802

#include "aw8680x_global.h"

#ifdef UART_TEST
	UART_STATUS_TYPE_E g_uart_status = UART_NOEVENT;
	AW_U8 g_uart_rx_buff[UART_BUFF_LEN] = {0};	// uart receive buff
	AW_U8 g_uart_data_cent = AW_NUM_NULL; // Used to send and receive counts.
#endif

/**
  * @brief  UART interrupt number configuration function
  * @param  uart_irq_pri_e is used to set interrupt priority
  * @retval None
  */
static void uart_irq_config(IRQ_PRI_E uart_irq_pri_e)
{
	NVIC_EnableIRQ(IRQ_UART); // Enable Interrupt
	NVIC_SetPriority(IRQ_UART, uart_irq_pri_e); // Set Interrupt Priority
}

/**
  * @brief  UART initialization function
  * @param  UART pointer to a UART_HANDLE_TYPE_S structure that contains
  *              the configuration information for the specified UART.
  * @retval RET_STATUS_E status
  */
RET_STATUS_E uart_init(UART_HANDLE_TYPE_S *p_uart_lib)
{
	AW_U32 tmp_flag = AW_NUM_NULL;
	AW_S32 uart_timeout = UART_TIMEOUT;

	/* Check the UART handle allocation. */
	if (p_uart_lib == AW_NULL) {
		return RET_ERROR;
	}

	assert_param(UART_CHECK_WORD_LEN(p_uart_lib->init_s.uart_word_lenth_e));

	uart_clk_init(); // Initialize serial clock
	uart_irq_config(p_uart_lib->uart_irq_pri_e); // Config uart interrupt number
	uart_pmux_init((AW_U8)p_uart_lib->init_s.gpio_rx,
						(AW_U8)p_uart_lib->init_s.gpio_tx); // IO pinmux is configured as UART mode

	/* Until the USART port is free. */
	while (UART->UART_STATUS & USR_BUSY_EN) {
		if((uart_timeout--) <= 0) {
			return RET_ERROR;
		}
	}

	UART->LINE_CTRL = LCR_DLAB_EN;  // Enable write divisor.

	/* Set UART port baud rate. */
	tmp_flag = (p_uart_lib->init_s.sys_clk / 16) / p_uart_lib->init_s.baud_rate;

	if (((p_uart_lib->init_s.sys_clk / 16) % p_uart_lib->init_s.baud_rate) >
		(p_uart_lib->init_s.baud_rate / 2)) {
		tmp_flag = tmp_flag + 1;
	}

	UART->INTEN = (AW_U8)(tmp_flag >> 8);
	UART->DIV_LL = (AW_U8)tmp_flag;

	uart_timeout = UART_TIMEOUT;
	/* Until the USART port is free. */
	while (UART->UART_STATUS & USR_BUSY_EN) {
		if((uart_timeout--) <= 0) {
			return RET_ERROR;
		}
	}

	/* Configure parity stop bit of serial port. */
	tmp_flag = p_uart_lib->init_s.uart_word_lenth_e | (p_uart_lib->init_s.stop_bit << LCR_STOP_BIT) |
				(p_uart_lib->init_s.pen_bit << LCR_PEN_BIT) | (p_uart_lib->init_s.eps_bit << LCR_EPS_BIT) |
				(p_uart_lib->init_s.stick_p_bit << LCR_STICK_P_BIT) | (p_uart_lib->init_s.bc_bit << LCR_BC_BIT);
	UART->LINE_CTRL = tmp_flag;

	/* Config UART FIFO control register. */
	tmp_flag = FCR_RCVR_HALF | FCR_TFT_HALF | FCR_XFIFOR_EN | FCR_RFIFOR_EN | FCR_FIFOE_EN;
	UART->FIFO_CTRL = tmp_flag; // Config fifo.
	UART->INTEN = IER_ERBFI_EN; // Receive data valid interrupt enable.

	return RET_OK;
}

/**
  * @brief  UART send one byte function
  * @param  AW_U8 send_data
  * @retval RET_STATUS_E status
  */
RET_STATUS_E uart_send_1byte(AW_U8 send_data)
{
	AW_S32 uart_timeout = UART_TIMEOUT;

	/* Wait for transmit FIFO is not full. */
	while ((UART->UART_STATUS & USR_TFNF_EN) == 0) {
		if((uart_timeout--) <= 0) {
			return RET_ERROR;
		}
	}

	UART->DIV_LL = send_data; // Send data
	uart_timeout = UART_TIMEOUT;

	/* Wait for sending to end. */
	while ((UART->LINE_STATUS) == 0) {
		if((uart_timeout--) <= 0) {
			return RET_ERROR;
		}
	}

	return RET_OK;
}

/**
  * @brief  UART send string function
  * @param  send_data is the starting address of sending data
  * @retval RET_STATUS_E status
  */
RET_STATUS_E uart_send_string(AW_U8 *p_send_data)
{
	/* Check the UART handle allocation. */
	if (p_send_data == AW_NULL) {
		return RET_ERROR;
	}

	while (*p_send_data != '\0') {
		uart_send_1byte(*p_send_data);
		p_send_data++;
	}

	return RET_OK;
}

/**
  * @brief  UART interrupt receive function
  * @param  None
  * @retval RET_STATUS_E status
  */
__weak RET_STATUS_E uart_irq(void)
{
	/* NOTE : This function Should not be modified, when the irq is needed,
			the aw_lib_timer_call could be implemented in the user file
	*/
#ifdef UART_TEST
	AW_U32 tmp_flag = AW_NUM_NULL;
	g_uart_status = LIB_UART_RX_BUSY; // UART current state is busy.

	tmp_flag = UART->UART_STATUS; // Read to clear busy interrupt.

	/* Check error. */
	if (UART->LINE_STATUS & LSR_RFE_EN) {
		/* Receive FIFO error. */
		tmp_flag = UART->LINE_STATUS;
		return RET_ERROR;
	}

	tmp_flag = UART->FIFO_CTRL;

	if ((tmp_flag & IIR_FIFOSE_EN) != IIR_FIFOSE_EN) {
		/* todo: fifo is not enabled. */
		return RET_ERROR;
	}

	if (((tmp_flag & IID_FULL) == IID_VALID) || ((tmp_flag & IID_FULL)) == IID_TIMEOUT) {
		while (UART->LINE_STATUS & LSR_DR_EN) { // The received data is ready
			g_uart_rx_buff[g_uart_data_cent] = UART->DIV_LL;
			g_uart_data_cent++;
		}

		/* Send back data if more than 8 bytes is recved. */
		if (g_uart_rx_buff[g_uart_data_cent] == '\0') {
			g_uart_status = LIB_UART_RX_END; // UART current state is finish.
			g_uart_data_cent = 0;
		}

		if (g_uart_data_cent >= UART_BUFF_LEN) {
			g_uart_status = LIB_UART_RX_END; // uart current state is finish
			g_uart_data_cent = 0;
		}
	}
#endif
	return RET_OK;
}
#endif